package org.example.controller;

import com.alibaba.excel.ExcelWriter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.example.dto.ExportTask;
import org.example.entity.User;
import org.example.service.UserService;
import org.example.util.ExcelUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.net.URLEncoder;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@RestController
public class ExportController {

    @Autowired
    private UserService userService;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    // 线程池处理异步任务（核心线程数根据服务器配置调整）
    private final ExecutorService exportExecutor = Executors.newFixedThreadPool(5);

    private static final ObjectMapper objectMapper = new ObjectMapper();


    /**
     * 方式一：传统的方式
     */
    @GetMapping("/export")
    public String export(HttpServletResponse response) throws IOException {
        Instant start = Instant.now();
        List<User> userList = userService.getAllUsers();
        ExcelUtil.export(response, userList, User.class, "用户列表");
        long seconds = Duration.between(start, Instant.now()).getSeconds();
        String format = String.format("导出用户完成！总条数：%d，耗时：%d秒", userList.size(), seconds);
        System.out.println(format);
        return format;
    }


    /**
     * 方式二：新的方式,用进度条的方式提示下载进度，但实际上还是单线程
     * 耗时：231秒左右
     */
    @GetMapping("/export/start")
    public String startExport() throws JsonProcessingException {

        redisTemplate.opsForValue().set("start_time",System.currentTimeMillis()+"");

        String taskId = "export_" + UUID.randomUUID().toString().replace("-", "");
        ExportTask task = new ExportTask();
        task.setTaskId(taskId);
        task.setProgress(0);
        task.setStatus("PROCESSING");
        task.setCreateTime(new Date());
        String taskStr = objectMapper.writeValueAsString(task);

        // 存储任务状态到Redis（过期时间：24小时）
        redisTemplate.opsForValue().set(taskId, taskStr, 24 * 3600, java.util.concurrent.TimeUnit.SECONDS);

        // 异步执行导出
        exportExecutor.submit(() -> doExport(taskId));
        return taskId;
    }

    // 2. 查询导出进度
    @GetMapping("/export/progress")
    public ExportTask getProgress(@RequestParam String taskId) {
        ExportTask task = getTask(taskId);
        return task;
    }

    // 3. 下载导出文件（任务完成后调用）
    @GetMapping("/export/download")
    public void downloadFile(@RequestParam String taskId, HttpServletResponse response) throws IOException {
        ExportTask task = getTask(taskId);
        if (task == null || !"COMPLETE".equals(task.getStatus())) {
            response.sendError(400, "任务未完成或不存在");
            return;
        }
        // 从临时路径读取文件并下载（实际项目中建议用Nginx托管静态文件）
        ExcelUtil.download(response, task.getFileUrl(), "用户列表.xlsx");
        String startTime = redisTemplate.opsForValue().get("start_time");
        long l = System.currentTimeMillis()- Long.parseLong(startTime);
        System.out.println("耗时："+ l/1000 +"秒");
    }

    // 核心：异步分批次导出逻辑
    // 核心：异步分批次导出逻辑（修正后）
    private void doExport(String taskId) {
        ExportTask task = getTask(taskId);
        ExcelWriter writer = null; // 声明写入器
        try {
            // 1. 获取总条数（用于计算进度）
            int total = userService.count();
            if (total == 0) {
                task.setProgress(100);
                task.setStatus("COMPLETE");
                writeTask(taskId, task);
                return;
            }

            // 2. 分批次查询并写入Excel（每批1万条）
            int batchSize = 10000;
            int totalPages = (total + batchSize - 1) / batchSize; // 总页数
            String tempFilePath = "/tmp/export_" + taskId + ".xlsx"; // 临时文件路径

            // 初始化Excel写入器（使用修正后的工具类）
            writer = ExcelUtil.initWriter(tempFilePath, User.class);

            for (int page = 0; page < totalPages; page++) {
                // 分页查询（page从0开始，MyBatis需支持分页参数）
                List<User> userList = userService.getUsersByPage(page, batchSize);

                // 写入当前批次数据：第一页写表头（isHead=true），后续页不写（isHead=false）
                ExcelUtil.write(writer, userList, page == 0);

                // 计算并更新进度
                int progress = (int) ((page + 1) * 100.0 / totalPages);
                task.setProgress(progress);
                writeTask(taskId, task);
            }

            // 3. 完成导出，关闭写入器
            ExcelUtil.finishWriter(writer);
            task.setProgress(100);
            task.setStatus("COMPLETE");
            task.setFileUrl(tempFilePath);

        } catch (Exception e) {
            // 处理异常情况
            task.setStatus("FAIL");
            task.setErrorMsg(e.getMessage());
            e.printStackTrace();
        } finally {
            // 确保写入器关闭
            ExcelUtil.finishWriter(writer);
            writeTask(taskId, task);
        }
    }

    private ExportTask getTask(String taskId) {
        String taskStr = redisTemplate.opsForValue().get(taskId);
        ExportTask task = null;
        try {
            task = objectMapper.readValue(taskStr, ExportTask.class);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
        return task;
    }

    private void writeTask(String taskId, ExportTask task) {
        String taskStr = null;
        try {
            taskStr = objectMapper.writeValueAsString(task);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
        redisTemplate.opsForValue().set(taskId, taskStr);
    }


}